/**
 *******************************************************************************
 * @FileName  : track.c
 * @Author    : GaoQiu
 * @CreateDate: 2020-02-18
 * @Copyright : Copyright(C) GaoQiu
 *              All Rights Reserved.
 *******************************************************************************
 *
 * The information contained herein is confidential and proprietary property of
 * GaoQiu and is available under the terms of Commercial License Agreement
 * between GaoQiu and the licensee in separate contract or the terms described
 * here-in.
 *
 * This heading MUST NOT be removed from this file.
 *
 * Licensees are granted free, non-transferable use of the information in this
 * file under Mutual Non-Disclosure Agreement. NO WARRENTY of ANY KIND is provided.
 *
 *******************************************************************************
 */
#include "track.h"
#include "libc/string.h"
#include "platform/platform.h"
#include "utils/heap.h"

void TrackInit(void)
{
#if TRACK_MODE == TRACK_MODE_USB
	USB_Init();
	USB_DebugInit();

#elif TRACK_MODE == TRACK_MODE_SUART
	SOFT_UART_Init(TRACK_PIN, TRACK_BAUDRATE);

#elif TRACK_MODE == TRACK_MODE_UART
	/* UART/DMA Clock config */
    RCC_EnablePeriphClk(RCC_UART|RCC_DMA);

    /**
     * !!!Note: pull-up setting must before UART GPIO config,
     * cause it will lead to ERR data to UART RX buffer
     */
    GPIO_SetPullModeEx(TRACK_PIN, GPIO_PullUp_Strong);//must, In order to stabilize and prevent leakage
    //GPIO_SetPullModeEx(HCI_TR_TX_PIN, GPIO_PullUp_Strong);//must, In order to stabilize and prevent leakage

    /* UART TX/RX Pin config */
    //GPIO_InitEx(HCI_TR_RX_PIN, GPIO_Mode_AF_In);
    //GPIO_SetAlternFuncEx(HCI_TR_RX_PIN, GPIO_AF_UART);

    GPIO_InitEx(TRACK_PIN, GPIO_Mode_AF_In);
    GPIO_SetAlternFuncEx(TRACK_PIN, GPIO_AF_UART);

    /* UART initialization */
	UART_InitType_t UART_InitStruct;
	UART_InitStruct.SysClkFreq      = 48000000;
	UART_InitStruct.UART_BaudRate   = TRACK_BAUDRATE;
	UART_InitStruct.UART_StopBits   = UART_StopBits_1;
	UART_InitStruct.UART_Parity     = UART_Parity_No;
	UART_InitStruct.UART_HwFlowCtrl = UART_HwFlowCtrl_None;
	UART_Init(&UART_InitStruct);

  #if 0
	/* UART Rx/Tx use DMA mode. */
	if(pRxBuf != NULL && byteSize != 0){
		UART_DmaConfig(pRxBuf, byteSize);
	}
	else{
		HCI_TR_TRACK_ERR("UART initialization failed...\n");
	}
	//UART_DmaITConfig(UART_DMA_IT_Rx_Tx, ENABLE);
	UART_DmaITConfig(UART_DMA_IT_Rx, ENABLE);
	IRQ_EnablePeriphIrq(IRQ_DMA);
	UART_DmaCmd(UART_DMA_Tx, ENABLE);
  #endif
#endif
}

void TrackStart(void)
{
#if TRACK_MODE == TRACK_MODE_USB
	USB_TaskStart();
#endif
}

void TrackPrint(const char *format, ...)
{
#if TRACK_MODE == TRACK_MODE_USB
	USB_Print(format);

#elif TRACK_MODE == TRACK_MODE_UART
	//printf(format);
	va_list va;
	va_start(va, format);
	char buffer[256] = {0};
	const int ret = PrintVsn(buffer, sizeof(buffer)-1, format, va);
	va_end(va);

	if(ret){
		UART_SendData((uint8_t *)buffer, ret);
	}

#elif TRACK_MODE == TRACK_MODE_SUART
	va_list va;
	va_start(va, format);

	char buffer[256] = {0};
	const int ret = PrintVsn(buffer, sizeof(buffer)-1, format, va);
	va_end(va);

	for(int i=0; i<ret; i++){
		SOFT_UART_PutChar(buffer[i]);
	}
#endif
}

void TrackData(uint8_t *pdata, uint32_t len)
{
#if TRACK_MODE == TRACK_MODE_USB
	USB_PrintData(pdata, len);

#elif TRACK_MODE == TRACK_MODE_UART
  #if 0
	char buf[256] = {0};
	TrackHexToString(buf, sizeof(buf)-1, pdata, len);
	TrackPrint("%s\r\n", buf);
  #endif

	uint8_t *p = pdata;

	while(len > 0)
	{
		uint32_t fragLen = len > 16 ? 16:len;
		char buf2[256] = {0};

		int cnt = TrackHexToString(buf2, sizeof(buf2)-1, p, fragLen);
		buf2[cnt] = 0;

		TrackPrint("\t%s\r\n", buf2);

		len -= fragLen;
		p += fragLen;
	}

#elif TRACK_MODE == TRACK_MODE_SUART
  #if 0
	char buf[256] = {0};
	TrackHexToString(buf, sizeof(buf)-1, pdata, len);
	TrackPrint("%s\r\n", buf);
  #else
	uint8_t *p = pdata;

	while(len > 0)
	{
		uint32_t fragLen = len > 16 ? 16:len;
		char buf2[256] = {0};

		int cnt = TrackHexToString(buf2, sizeof(buf2)-1, p, fragLen);
		buf2[cnt] = 0;
		TrackPrint("%s\r\n", buf2);

		len -= fragLen;
		p += fragLen;
	}
  #endif
#endif
}

void TrackStringData(const char *txt, uint8_t *pdata, uint32_t len)
{
#if TRACK_MODE == TRACK_MODE_USB
	USB_PrintStringAndData(txt, pdata, len);

#elif TRACK_MODE == TRACK_MODE_UART
	char buf[256] = {0};
	int cnt = TrackHexToString(buf, sizeof(buf)-1, pdata, len);
	buf[cnt] = 0;

	TrackPrint("%s ", txt);
	TrackPrint("%s\r\n", buf);

#elif TRACK_MODE == TRACK_MODE_SUART
	char buf[256] = {0};
	TrackHexToString(buf, sizeof(buf)-1, pdata, len);
	TrackPrint("%s ", txt);
	TrackPrint("%s\r\n", buf);

#endif
}

uint32_t TrackHexToString(char *pStr, uint32_t bufSize, uint8_t *pData, uint32_t len)
{
	uint32_t cnt = 0;
	uint8_t ascii[] = "0123456789ABCDEF";

	for(int i=0; i<len; i++)
	{
		if(cnt + 3 >= bufSize){
			break;
		}

		uint8_t h = (pData[i]>>4) & 0x0f;
		uint8_t l = pData[i] & 0x0f;

		pStr[cnt++] = ascii[h];
		pStr[cnt++] = ascii[l];
		pStr[cnt++] = ' ';

	#if 0
		if(h>=0 && h<=9){
			pStr[cnt++] = '0' + h;
		}
		else if((h>=0x0a && h<=0x0f)){
			pStr[cnt++] = 'A' + h - 0x0a;
		}

		if(l>=0 && l<=9){
			pStr[cnt++] = '0' + l;
		}
		else if((l>=0x0a && l<=0x0f)){
			pStr[cnt++] = 'A' + l - 0x0a;
		}
		pStr[cnt++] = ' ';
	#endif
	}

	return cnt;
}


void TrackPrintSystemInfo(void)
{
#if TRACK_MODE != TRACK_MODE_NONE
	extern char _end_bss_[];
	extern char __ram_bottom[];
	TRACK("Heap Free Size: %d B\r\n", HeapGetAvailableSize());
	TRACK("RAM Free: 0x%08x, %d B\r\n", _end_bss_, __ram_bottom - _end_bss_);
	TRACK("FHS:%d Hz; System Clock:%d Hz\r\n", RCC_GetFhsClk(), RCC_GetSysClk());
#endif
}

/**************************************************************************************************
  Macros
**************************************************************************************************/

/*! \brief    Detect hexadecimal digits. */
#define PRINT_IS_XDIGIT(c)     (((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f')) || \
                                ((c >= 'A') && (c <= 'F')))

/*! \brief    Convert hexadecimal digit to integer. */
#define PRINT_XDIGIT_TO_INT(c)  (((c >= '0') && (c <= '9')) ? (uint8_t)(c - '0') : \
                                ((c >= 'a') && (c <= 'f')) ? (uint8_t)(c - 'a' + 10u) : \
                                ((c >= 'A') && (c <= 'F')) ? (uint8_t)(c - 'A' + 10u) : 0u)

/*! HEX format '0x' or '0X' check */
#define WSTR_IS_HEX_FORMAT(c) (((char)(c)[0] == '0') && \
                               (((char)(c)[1] == 'x') || ((char)(c)[1] == 'X')))

/*! Binary format '0b' check */
#define WSTR_IS_BIN_FORMAT(c)  (((char)(c)[0] == '0') && ((char)(c)[1] == 'b'))

/*************************************************************************************************/
/*!
 *  \brief  Copy a string and zero out space after the string length.
 *
 *  \return none.
 */
/*************************************************************************************************/
void WstrnCpy(char *pBuf, const char *pData, uint8_t n)
{
  uint8_t i;
  uint8_t zeroing = FALSE;

  for (i=0; i<n; i++)
  {
    if (!zeroing && pData[i] == '\0')
      zeroing = TRUE;

    if (zeroing)
      *pBuf++ = 0;
    else
      *pBuf++ = pData[i];
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Byte by byte reverse and copy a buffer.
 *
 *  \param  pBuf1   Buffer to hold reversed copy.
 *  \param  pBuf2   Buffer to copy.
 *  \param  len     Size of pBuf1 and pBuf2 in bytes.
 *
 *  \return None.
 */
/*************************************************************************************************/
void WStrReverseCpy(uint8_t *pBuf1, const uint8_t *pBuf2, uint16_t len)
{
  int16_t i;

  for (i=0; i<len; i++)
  {
    pBuf1[len-1-i] = pBuf2[i];
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Byte by byte reverse a buffer.
 *
 *  \param  pBuf   Buffer to reverse.
 *  \param  len    size of pBuf in bytes.
 *
 *  \return None.
 */
/*************************************************************************************************/
void WStrReverse(uint8_t *pBuf, uint8_t len)
{
  uint8_t i, temp;

  for (i=0; i<len/2; i++)
  {
    temp = pBuf[len-i-1];
    pBuf[len-i-1] = pBuf[i];
    pBuf[i] = temp;
  }
}

/*************************************************************************************************/
/*!
 *  \brief  Format a hex value.
 *
 *  \param  pBuf    Storage for string representation of value.
 *  \param  val     Value.
 *  \param  len     Length of value, in bits.
 *
 *  \return     None.
 */
/*************************************************************************************************/
void WStrFormatHex(char *pBuf, uint32_t val, uint8_t len)
{
  uint32_t i;
  uint8_t  lenChars = (len + 3) / 4;

  for (i = 0; i < lenChars; i++)
  {
    uint8_t n = val & 0x0F;
    pBuf[(lenChars - 1) - i] = (n < 10) ? ('0' + n) : ('A' + (n - 10));
    val >>= 4;
  }
}

/*************************************************************************************************/
/*!
 *  \fn     WStrHexToArray
 *
 *  \brief  Convert a hex string to int array, zero out space after the string length.
 *
 *  \param  pStr    Pointer to the string to convert.
 *  \param  pBuf    Pointer to destination buffer.
 *  \param  len     Size of pBuf in bytes.
 *
 *  \return none.
 */
/*************************************************************************************************/
void WStrHexToArray(const char *pStr, uint8_t *pBuf, uint16_t len)
{
  uint16_t i;
  uint8_t val = 0;
  uint8_t j = 0;
  bool_t zeroing = FALSE;

  /* Skip white spaces. */
  while (*pStr == ' ' || *pStr == '\t' || *pStr == '\n' || *pStr == '\r')
  {
    ++pStr;
  }

  /* Check for hex value format (0x or 0X). */
  if (WSTR_IS_HEX_FORMAT(pStr))
  {
    pStr += 2;
  }

  for (i = 0; i < len; i++)
  {
    if (!zeroing && *pStr == '\0')
    {
      zeroing = TRUE;
    }

    if (zeroing)
    {
      *pBuf++ = 0;
    }
    else
    {
      while (j < 2)
      {
        if ((*pStr >= '0') && (*pStr <= '9'))
        {
          val = (val << (j << 2)) | (*pStr - '0');
        }
        else if ((*pStr >= 'a') && (*pStr <= 'f'))
        {
          val = (val << (j << 2)) | (10 + (*pStr - 'a'));
        }
        else if ((*pStr >= 'A') && (*pStr <= 'F'))
        {
          val = (val << (j << 2)) | (10 + (*pStr - 'A'));
        }
        else
        {
          break;
        }

        pStr++;

        /* Check if value is computed and needs to be added to destination buffer. */
        if (j++ == 1)
        {
          *pBuf++ = val;
        }
      }

      j = 0;
      val = 0;
    }
  }
}



/*************************************************************************************************/
/*!
 *  \brief  Format an integer.
 *
 *  \param  pStr    Storage for formatted integer.
 *  \param  maxLen  Maximum number of characters to store.
 *  \param  i       Integer to format.
 *  \param  base    Integer base.
 *  \param  sign    TRUE if sign should be printed.
 *  \param  width   Width of field.
 *
 *  \return Number of characters stored.
 */
/*************************************************************************************************/
static int printFmtInt(char *pStr, int maxLen, int i, uint8_t base, bool_t sign, int width)
{
  char *s, *p = pStr;
  unsigned int u = (unsigned int) i;
  uint8_t use_width;
  int t;

  use_width = width;

  if (i == 0)
  {
    s = "0\0";
    width--;
    goto almost;
  }

  if (sign && base == 10 && i < 0)
  {
    *pStr++ = '-';
    u = (unsigned int)-i;
    width--;
  }
  s = pStr + maxLen - 1;
  *s = '\0';

  while (u && (!use_width || (width > 0)))
  {
    t = (unsigned int)u % base;
    if (t >= 10)
    {
      t += 'A' - '0' - 10;
    }
    *--s = (char)(t + '0');
    u /= base;
    width--;
  }

almost:
  while (width > 0)
  {
    *pStr++ = '0';
    width--;
  }
  strcpy(pStr, s);

  return strlen(p);
}

/*************************************************************************************************/
/*!
 *  \brief  Parse an integer from a string.
 *
 *  \param  pStr      String to parse.
 *  \param  pInt      Storage for parsed integer.
 *  \param  base      Integer base.
 *
 *  \return Number of characters consumed.
 */
/*************************************************************************************************/
static int32_t printParseInt(const char *pStr, uint32_t *pInt, uint32_t base)
{
  int32_t r = 0;

  if (base == 0)
  {
    if (*pStr == '0')
    {
      if (((*(pStr + 1) == 'x') || (*(pStr + 1) == 'X')) && PRINT_IS_XDIGIT(*(pStr + 2)))
      {
        r    += 2;
        pStr += 2;
        base  = 16;
      }
      else
      {
        base = 8;
      }
    }
    else
    {
      base = 10;
    }
  }
  else if (base == 16)
  {
    if (*pStr == '0')
    {
      if (((*(pStr + 1) == 'x') || (*(pStr + 1) == 'X')) && PRINT_IS_XDIGIT(*(pStr + 2)))
      {
        r    += 2;
        pStr += 2;
      }
    }
  }

  /* One character is required; all characters must be consumed. */
  *pInt = 0;
  do
  {
    char    c;
    uint8_t t;

    c = *pStr++;
    if (!PRINT_IS_XDIGIT(c))
    {
      break;
    }

    t = PRINT_XDIGIT_TO_INT(c);
    if (t >= base)
    {
      break;
    }

    *pInt *= base;
    *pInt += t;
    r++;
  } while (*pStr != '\0');

  return r;
}

/*************************************************************************************************/
/*!
 *  \brief  Print a trace message.
 *
 *  \param  pStr      Storage for formatted string.
 *  \param  size      Maximum number of characters to store.
 *  \param  pFmt      Format string.
 *  \param  ap        Arguments.
 *
 *  \return Number of characters stored.
 */
/*************************************************************************************************/
uint32_t PrintVsn(char *pStr, uint32_t size, const char *pFmt, va_list ap)
{
    size_t len = 0;

    *pStr = 0;
    size--; /* Ensure we null-terminate within our buffer */

    while ((*pFmt != '\0') && (len < size))
    {
      uint32_t width = 0;

      /* normal */
      if (*pFmt != '%')
      {
        *pStr++ = *pFmt++;
        len++;
        continue;
      }

      pFmt++;
      if (*pFmt == '%')
      {
        *pStr++ = '%';
        len++;
        pFmt++;
        continue;
      }

      /* width */
      if (*pFmt == '0')
      {
        pFmt += printParseInt(pFmt, &width, 10u);
      }

      /* long (ignore) */
      if (*pFmt == 'l')
      {
        pFmt++;
      }

      switch(*pFmt)
      {
        /* character */
        case 'c':
        {
          char tmp = va_arg(ap, int);
          *pStr++  = tmp;
          len++;
          break;
        }
        /* unsigned decimal integer */
        case 'u':
        {
          unsigned int tmp    = va_arg(ap, unsigned int);
          uint8_t      lenTmp = printFmtInt(pStr, size - len, tmp, 10, 0, width);
          pStr += lenTmp;
          len  += lenTmp;
          break;
        }
        /* signed decimal integer */
        case 'd':
        {
          int     tmp    = va_arg(ap, int);
          uint8_t lenTmp = printFmtInt(pStr, size - len, tmp, 10, 1, width);
          pStr += lenTmp;
          len  += lenTmp;
          break;
        }
        /* pointer */
        case 'p':
        {
          unsigned long tmp    = va_arg(ap, unsigned long);
          uint8_t       lenTmp = printFmtInt(pStr, size - len, tmp, 16, 1, 8u);
          pStr += lenTmp;
          len  += lenTmp;
          break;
        }
        /* unsigned hexadecimal integer */
        case 'x':
        case 'X':
        {
          unsigned int tmp    = va_arg(ap, unsigned int);
          uint8_t      lenTmp = printFmtInt(pStr, size - len, tmp, 16, 0, width);
          pStr += lenTmp;
          len  += lenTmp;
          break;
        }
        /* string */
        case 's':
        {
          char *tmp = va_arg(ap, char *);
          while ((tmp != NULL) && (*tmp != '\0') && (len < size))
          {
            *pStr++ = *tmp++;
            len++;
          }
          break;
        }
        default:
          break;
      }
      pFmt++;
    }

    /* Null-terminate output. */
    *pStr = 0;

    if (len > size)
    {
      /* Compensate for -1 earlier. */
      return size + 2;
    }

    return len;
}

/**
 *******************************************************************************
 * ASSERT
 *******************************************************************************
 */
/**
 * 	char fn[30],*p;
 *  strcpy(fn,(p=strrchr(pcFile,'/')) ? p+1 : pcFile);
 */
void assert_failed(uint8_t exp, uint32_t errCode, char *fileName, int line)
{
	if(exp == 0){
		TRACK_ERR("ASSERT: File: %s Line: %d Error:0x%08x\r\n", fileName, line, errCode);
		while(1) { TRACK_START(); }
	}
}
